package com.sky.aop;

import com.sky.annotation.AutoFill;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author Administrator
 * @version 1.0
 * @description: TODO
 * @date 2023/9/19 22:51
 */
@Aspect
@Component
@Slf4j
public class AutoFillAspect {

    //1、定义切入点表达式
    @Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")
    public void doAspect(){};

    //2、定义前置通知
    @Before("doAspect() && @annotation(autoFill)")
    public void before(JoinPoint joinPoint, AutoFill autoFill) throws InvocationTargetException, IllegalAccessException {
        //1、获取数据库操作类型
        OperationType operationType = autoFill.value();

        //2、获取要操作的数据库对象
        Object[] args = joinPoint.getArgs();
        if(ObjectUtils.isEmpty(args)){
            return;
        }

        Object entity = args[0];

        //3、根据数据库类型填充属性
        Class<?> aClass = entity.getClass();
        Map<String, Method> methodMap = Arrays.stream(aClass.getDeclaredMethods()).collect(Collectors.toMap(f -> f.getName(), Function.identity()));

        Map<String, Field> fieldMap = Arrays.stream(aClass.getDeclaredFields()).collect(Collectors.toMap(f -> f.getName(), Function.identity()));


        if(OperationType.INSERT == operationType){
            //1、填充创建时间
            /*fillField1(entity, methodMap, "setCreateTime");
            fillField1(entity, methodMap, "setCreateUser");
            fillField1(entity, methodMap, "setUpdateTime");
            fillField1(entity, methodMap, "setUpdateUser");*/

            fillField2(entity,fieldMap,"createTime");
            fillField2(entity,fieldMap,"createUser");
            fillField2(entity,fieldMap,"updateTime");
            fillField2(entity,fieldMap,"updateUser");
        }else{
            /* fillField1(entity, methodMap, "setUpdateTime");
            fillField1(entity, methodMap, "setUpdateUser");*/

            fillField2(entity, fieldMap, "updateTime");
            fillField2(entity, fieldMap, "updateUser");
        }
    }

    /**
     * 使用方法进行反射填充属性
     * @param entity
     * @param methodMap
     * @param methodName
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private void fillField1(Object entity, Map<String, Method> methodMap,String methodName) throws IllegalAccessException, InvocationTargetException {
        Method method = methodMap.get(methodName);
        if(ObjectUtils.isEmpty(method)){
            return;
        }
        if(methodName.contains("Time")){
            method.invoke(entity, LocalDateTime.now());
        }else{
            method.invoke(entity, BaseContext.getCurrentId());
        }
    }

    /**
     * 使用字段进行反射填充属性
     * @param entity
     * @param fieldMap
     * @param fieldName
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     */
    private void fillField2(Object entity, Map<String, Field> fieldMap,String fieldName) throws IllegalAccessException, InvocationTargetException {
        Field field = fieldMap.get(fieldName);
        if(ObjectUtils.isEmpty(field)){
            return;
        }
        field.setAccessible(true);
        if(field.getType() == LocalDateTime.class){
            field.set(entity,LocalDateTime.now());
        }else{
            field.set(entity,BaseContext.getCurrentId());
        }
    }

}
